 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import java.util.Hashtable ;

 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.jface.viewers.IPostSelectionProvider;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.ui.INullSelectionListener;
 import org.eclipse.ui.ISelectionListener;
 import org.eclipse.ui.ISelectionService;
 import org.eclipse.ui.IWorkbenchPart;

 /**
  * Abstract selection service.
  */
 public abstract class AbstractSelectionService implements ISelectionService {

     /**
      * The list of selection listeners (not per-part).
      */
     private ListenerList listeners = new ListenerList();

     /**
      * The list of post selection listeners (not per-part).
      */
     private ListenerList postListeners = new ListenerList();

     /**
      * The currently active part.
      */
     private IWorkbenchPart activePart;

     /**
      * The active part's selection provider, remembered in case the part
      * replaces its selection provider after we hooked a listener.
      */
     private ISelectionProvider activeProvider;

     /**
      * Map from part id (String) to per-part tracker (AbstractPartSelectionTracker).
      */
     private Hashtable perPartTrackers;

     /**
      * The JFace selection listener to hook on the active part's selection provider.
      */
     private ISelectionChangedListener selListener = new ISelectionChangedListener() {
         public void selectionChanged(SelectionChangedEvent event) {
             fireSelection(activePart, event.getSelection());
         }
     };

     /**
      * The JFace post selection listener to hook on the active part's selection provider.
      */
     private ISelectionChangedListener postSelListener = new ISelectionChangedListener() {
         public void selectionChanged(SelectionChangedEvent event) {
             firePostSelection(activePart, event.getSelection());
         }
     };

     /**
      * Creates a new SelectionService.
      */
     protected AbstractSelectionService() {
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void addSelectionListener(ISelectionListener l) {
         listeners.add(l);
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void addSelectionListener(String partId, ISelectionListener listener) {
         getPerPartTracker(partId).addSelectionListener(listener);
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void addPostSelectionListener(ISelectionListener l) {
         postListeners.add(l);
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void addPostSelectionListener(String partId,
             ISelectionListener listener) {
         getPerPartTracker(partId).addPostSelectionListener(listener);
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void removeSelectionListener(ISelectionListener l) {
         listeners.remove(l);
     }

     /*
      * (non-Javadoc)
      * Method declared on ISelectionListener.
      */
     public void removePostSelectionListener(String partId,
             ISelectionListener listener) {
         getPerPartTracker(partId).removePostSelectionListener(listener);
     }

     /* (non-Javadoc)
      * Method declared on ISelectionService.
      */
     public void removePostSelectionListener(ISelectionListener l) {
         postListeners.remove(l);
     }

     /*
      * (non-Javadoc)
      * Method declared on ISelectionListener.
      */
     public void removeSelectionListener(String partId,
             ISelectionListener listener) {
         getPerPartTracker(partId).removeSelectionListener(listener);
     }

     /**
      * Fires a selection event to the given listeners.
      *
      * @param part the part or <code>null</code> if no active part
      * @param sel the selection or <code>null</code> if no active selection
      */
     protected void fireSelection(final IWorkbenchPart part, final ISelection sel) {
         Object [] array = listeners.getListeners();
         for (int i = 0; i < array.length; i++) {
             final ISelectionListener l = (ISelectionListener) array[i];
             if ((part != null && sel != null)
                     || l instanceof INullSelectionListener) {
                 
                 try {
                     l.selectionChanged(part, sel);
                 } catch (Exception e) {
                     WorkbenchPlugin.log(e);
                 }
             }
         }
     }

     /**
      * Fires a selection event to the given listeners.
      *
      * @param part the part or <code>null</code> if no active part
      * @param sel the selection or <code>null</code> if no active selection
      */
     protected void firePostSelection(final IWorkbenchPart part,
             final ISelection sel) {
         Object [] array = postListeners.getListeners();
         for (int i = 0; i < array.length; i++) {
             final ISelectionListener l = (ISelectionListener) array[i];
             if ((part != null && sel != null)
                     || l instanceof INullSelectionListener) {
                 
                 try {
                     l.selectionChanged(part, sel);
                 } catch (Exception e) {
                     WorkbenchPlugin.log(e);
                 }
             }
         }
     }

     /**
      * Returns the per-part selection tracker for the given part id.
      *
      * @param partId part identifier
      * @return per-part selection tracker
      */
     protected AbstractPartSelectionTracker getPerPartTracker(String partId) {
         if (perPartTrackers == null) {
             perPartTrackers = new Hashtable (4);
         }
         AbstractPartSelectionTracker tracker = (AbstractPartSelectionTracker) perPartTrackers
                 .get(partId);
         if (tracker == null) {
             tracker = createPartTracker(partId);
             perPartTrackers.put(partId, tracker);
         }
         return tracker;
     }

     /**
      * Creates a new per-part selection tracker for the given part id.
      *
      * @param partId part identifier
      * @return per-part selection tracker
      */
     protected abstract AbstractPartSelectionTracker createPartTracker(
             String partId);

     /**
      * Returns the selection.
      */
     public ISelection getSelection() {
         if (activeProvider != null) {
             return activeProvider.getSelection();
         } else {
             return null;
         }
     }

     /*
      * @see ISelectionService#getSelection(String)
      */
     public ISelection getSelection(String partId) {
         return getPerPartTracker(partId).getSelection();
     }

     /**
      * Sets the current-active part (or null if none)
      *
      * @since 3.1
      *
      * @param newPart the new active part (or null if none)
      */
     public void setActivePart(IWorkbenchPart newPart) {
         // Optimize.
 if (newPart == activePart) {
             return;
         }
         
         ISelectionProvider selectionProvider = null;
         
         if (newPart != null) {
             selectionProvider = newPart.getSite().getSelectionProvider();
             
             if (selectionProvider == null) {
                 newPart = null;
             }
         }
         
         if (newPart == activePart) {
             return;
         }
         
         if (activePart != null) {
             if (activeProvider != null) {
                 activeProvider.removeSelectionChangedListener(selListener);
                 if (activeProvider instanceof IPostSelectionProvider) {
                     ((IPostSelectionProvider) activeProvider)
                             .removePostSelectionChangedListener(postSelListener);
                 } else {
                     activeProvider
                             .removeSelectionChangedListener(postSelListener);
                 }
                 activeProvider = null;
             }
             activePart = null;
         }

         activePart = newPart;
         
         if (newPart != null) {
             activeProvider = selectionProvider;
             // Fire an event if there's an active provider
 activeProvider.addSelectionChangedListener(selListener);
             ISelection sel = activeProvider.getSelection();
             fireSelection(newPart, sel);
             if (activeProvider instanceof IPostSelectionProvider) {
                 ((IPostSelectionProvider) activeProvider)
                         .addPostSelectionChangedListener(postSelListener);
             } else {
                 activeProvider.addSelectionChangedListener(postSelListener);
             }
             firePostSelection(newPart, sel);
         } else {
             fireSelection(null, null);
             firePostSelection(null, null);
         }
     }
     
 // /**
 // * Notifies the listener that a part has been activated.
 // */
 // public void partActivated(IWorkbenchPart newPart) {
 // // Optimize.
 // if (newPart == activePart)
 // return;
 //
 // // Unhook selection from the old part.
 // reset();
 //
 // // Update active part.
 // activePart = newPart;
 //
 // // Hook selection on the new part.
 // if (activePart != null) {
 // activeProvider = activePart.getSite().getSelectionProvider();
 // if (activeProvider != null) {
 // // Fire an event if there's an active provider
 // activeProvider.addSelectionChangedListener(selListener);
 // ISelection sel = activeProvider.getSelection();
 // fireSelection(newPart, sel);
 // if (activeProvider instanceof IPostSelectionProvider)
 // ((IPostSelectionProvider) activeProvider)
 // .addPostSelectionChangedListener(postSelListener);
 // else
 // activeProvider.addSelectionChangedListener(postSelListener);
 // firePostSelection(newPart, sel);
 // } else {
 // //Reset active part. activeProvider may not be null next time this method is called.
 // activePart = null;
 // }
 // }
 // // No need to fire an event if no active provider, since this was done in reset()
 // }
 //
 // /**
 // * Notifies the listener that a part has been brought to the front.
 // */
 // public void partBroughtToTop(IWorkbenchPart newPart) {
 // // do nothing, the active part has not changed,
 // // so the selection is unaffected
 // }
 //
 // /**
 // * Notifies the listener that a part has been closed
 // */
 // public void partClosed(IWorkbenchPart part) {
 // // Unhook selection from the part.
 // if (part == activePart) {
 // reset();
 // }
 // }
 //
 // /**
 // * Notifies the listener that a part has been deactivated.
 // */
 // public void partDeactivated(IWorkbenchPart part) {
 // // Unhook selection from the part.
 // if (part == activePart) {
 // reset();
 // }
 // }
 //
 // /**
 // * Notifies the listener that a part has been opened.
 // */
 // public void partOpened(IWorkbenchPart part) {
 // // Wait for activation.
 // }
 //
 // /**
 // * Notifies the listener that a part has been opened.
 // */
 // public void partInputChanged(IWorkbenchPart part) {
 // // 36501 - only process if part is active
 // if (activePart == part) {
 // reset();
 // partActivated(part);
 // }
 // }
 //
 // /**
 // * Resets the service. The active part and selection provider are
 // * dereferenced.
 // */
 // public void reset() {
 // if (activePart != null) {
 // fireSelection(null, null);
 // firePostSelection(null, null);
 // if (activeProvider != null) {
 // activeProvider.removeSelectionChangedListener(selListener);
 // if (activeProvider instanceof IPostSelectionProvider)
 // ((IPostSelectionProvider) activeProvider)
 // .removePostSelectionChangedListener(postSelListener);
 // else
 // activeProvider
 // .removeSelectionChangedListener(postSelListener);
 // activeProvider = null;
 // }
 // activePart = null;
 // }
 // }

 }

